package com.guojian.jdbc.mapper.bean;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import javax.persistence.Column;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.JdbcUtils;

import com.guojian.jdbc.JdbcDataFormat;


/**
 * 参考 @BeanPropertyRowMapper
 * 数据库记录映射成为entity
 * 1.在 @BeanPropertyRowMapper 的基础上增加了 @Column 注解来自定义列名称
 * 
 * 规则1: 字段 abc -- 列名 ABC
 * 规则2: 字段 abcD --列名 abc_d ,大写会转成_
 * 规则3: 使用 @Column 指定列名
 * 
 * 全部不区分大小写, 统一按照小写进行匹配
 * 
 * @author GuoJian
 *
 */
public class BeanPropertyDataFormatRowMapper<T> implements RowMapper<T> {

	private Class<T> mappedClass;

	private Map<String, Field> mappedFields = new HashMap<String, Field>();

	private JdbcDataFormat<T> dataFormat;

	public BeanPropertyDataFormatRowMapper(Class<T> mappedClass) {
		this(mappedClass, null);
	}

	public BeanPropertyDataFormatRowMapper(Class<T> mappedClass, JdbcDataFormat<T> dataFormat) {
		this.dataFormat = dataFormat;
		this.mappedClass = mappedClass;
		for (Field field : this.mappedClass.getDeclaredFields()) {
			String lowerCaseName = lowerCaseName(getFieldName(field));
			this.mappedFields.put(lowerCaseName, field);
			String underscoredName = underscoreName(field.getName());
			if (!lowerCaseName.equals(underscoredName)) {
				this.mappedFields.put(underscoredName, field);
			}
		}
	}

	@Override
	public T mapRow(ResultSet rs, int rowNumber) throws SQLException {
		T mappedObject = BeanUtils.instantiateClass(this.mappedClass);

		ResultSetMetaData rsmd = rs.getMetaData();
		int columnCount = rsmd.getColumnCount();
		for (int i = 1; i <= columnCount; i++) {
			String columnName = JdbcUtils.lookupColumnName(rsmd, i);
			Object columnValue = getColumnValue(rs, i);
			String fieldName = lowerCaseName(StringUtils.trim(columnName));
			Field field = this.mappedFields.get(fieldName);
			if (field == null) {
				continue;
			}
			field.setAccessible(true);
			try {
				field.set(mappedObject, columnValue);
			} catch (IllegalArgumentException | IllegalAccessException e) {
				throw new SQLException(e);
			}
		}

		if (this.dataFormat != null) {
			dataFormat.formatRow(mappedObject);
		}

		return mappedObject;
	}

	protected String getFieldName(Field field) {
		Column keyAnnotation = field.getAnnotation(Column.class);
		if (keyAnnotation != null && StringUtils.isNotBlank(keyAnnotation.name())) {
			return keyAnnotation.name();
		}
		return field.getName();
	}

	protected String lowerCaseName(String name) {
		return name.toLowerCase(Locale.US);
	}

	protected String underscoreName(String name) {
		if (StringUtils.isBlank(name)) {
			return StringUtils.EMPTY;
		}

		StringBuilder result = new StringBuilder();
		for (int i = 0; i < name.length(); i++) {
			char c = name.charAt(i);
			if (Character.isUpperCase(c)) {
				result.append('_').append(Character.toLowerCase(c));
			} else {
				result.append(c);
			}
		}
		return result.toString();
	}

	protected Object getColumnValue(ResultSet rs, int index) throws SQLException {
		return JdbcUtils.getResultSetValue(rs, index);
	}

	public static void main(String[] args) {
	}
}
